package org.msb.accentis.gui.views;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.msb.accentis.data.CategoryType;
import org.msb.accentis.data.Database;
import org.msb.accentis.data.IDataManager;
import org.msb.accentis.data.IDataManagerListener;
import org.msb.accentis.data.QueryParameterMap;
import org.msb.accentis.data.pojo.Category;
import org.msb.accentis.gui.models.PojoTreeModel;

public class CategoryTreeModel extends PojoTreeModel<Category> {

	private Map<CategoryType, List<Category>> categoryTree = new HashMap<CategoryType, List<Category>>();
	
	public CategoryTreeModel() {
		super("Category.getAllCategories", Category.class);
		
		for (CategoryType type : CategoryType.values())
			categoryTree.put(type, new ArrayList<Category>());
	}
	
	@Override
	public Object getChild(Object parent, int index) {
		if (getRoot().equals(parent))
			return CategoryType.values()[index];
		
		if (parent.getClass() == CategoryType.class)
			return categoryTree.get(parent).get(index);
		
		if (parent instanceof Category)
			return ((Category) parent).getSubCategories().get(index);
		
		return null;
	}

	@Override
	public int getChildCount(Object parent) {
		if (getRoot().equals(parent))
			return CategoryType.values().length;
		
		if (parent.getClass() == CategoryType.class) {
			List<Category> list = categoryTree.get(parent);
			return list.size();
		}
		
		if (parent instanceof Category)
			return ((Category) parent).getSubCategories().size();

		return 0;
	}

	@Override
	public boolean isLeaf(Object node) {
		if (node instanceof Category)
			return ((Category) node).isSubCategory();
		
		return false;
	}

	@Override
	public int getIndexOfChild(Object parent, Object child) {
		if (getRoot().equals(parent))
			return getIndexOfCategoryType((CategoryType) child);
		
		if (parent.getClass() == CategoryType.class)
			return categoryTree.get(parent).indexOf(child);
		
		if (parent instanceof Category)
			return ((Category) parent).getSubCategories().indexOf(child);
		
		return -1;
	}
	
	private int getIndexOfCategoryType(CategoryType type) {
		for (int i = 0; i < CategoryType.values().length; ++i)
			if (CategoryType.values()[i].equals(type))
				return i;
		
		return -1;
	}

	@Override
	protected void load(IDataManager dataManager) {
		List<Category> list = dataManager.runNamedQuery(getQueryName(), getQueryParameters());
		dataManager.addDataManagerListener(getDataManagerListener(), Category.class);
		
		for (Category category : list)
			addCategoryToTree(category);
		
		fireTreeContentChanged();
	}
	
	private void addCategoryToTree(Category category) {
		CategoryType type = category.getCategoryType();
		List<Category> categorySubTree = categoryTree.get(type);
		
		if (null == categorySubTree) {
			categorySubTree = new ArrayList<Category>();
			categoryTree.put(type, categorySubTree);
		}
		
		categorySubTree.add(category);
	}

	protected IDataManagerListener<Category> getDataManagerListener() {
		return new IDataManagerListener<Category>() {
			@Override
			public void objectCreated(Category object) {
				List<Object> pathList = new ArrayList<Object>();
				pathList.add(getRoot());
				pathList.add(object.getCategoryType());
				
				int[] childIndex = new int[1];
				Object[] child = new Object[] { object };
				
				if (object.isSubCategory()) {
					pathList.add(object.getParentCategory());
					childIndex[0] = object.getParentCategory().getSubCategories().indexOf(object);
				} else {
					List<Category> list = categoryTree.get(object.getCategoryType());
					int pos = Collections.binarySearch(list, object);
					
					if (0 > pos) {
						pos = -pos - 1;
						list.add(pos, object);
						childIndex[0] = pos;
					}
				}
				
				fireTreeNodesInserted(pathList.toArray(), childIndex, child);
			}

			@Override
			public void objectRemoved(Category object) {
				List<Object> pathList = new ArrayList<Object>();
				pathList.add(getRoot());
				pathList.add(object.getCategoryType());
				
				int[] childIndex = new int[1];
				Object[] child = new Object[] { object };
				
				if (object.isSubCategory()) {
					pathList.add(object.getParentCategory());
					childIndex[0] = -Collections.binarySearch(object.getParentCategory().getSubCategories(), object) - 1;
				}
				
				fireTreeNodesRemoved(pathList.toArray(), childIndex, child);
			}

			@Override
			public void objectSaved(Category object) {
				
			}

			@Override
			public void objectRefreshed(Category object) {
			}
		};
	}

	@Override
	public void databaseChanged(Database database) {
		reload(QueryParameterMap.NO_PARAMETERS);
	}
	
	@Override
	public void clear() {
		for (List<Category> list : categoryTree.values())
			list.clear();
	}
}
